 //
//  MRMessageCenter.m
//  McRemoteServer
//
//  Created by Magic on 1/8/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import "MRMessageCenter.h"
#import "MRServer.h"
#import "MRMessage.h"
#import "MREventCenter.h"
#import "DockManager.h"

#define kMessageTypeTag                 1000
#define kMessageBinarySizeTag           1001
#define kMessageBinaryBodyTag           1002
#define kMessageKeyStrokeTag            1003
#define kMessageKeyStrokeWithFlagTag    1004
#define kMessageMacOSVersionTag         1005
#define kMessageMouseMoveTag            1006
#define kMessageMouseWheelScrollTag     1007

#define kMessageIsRetinaTag             1008
#define kMessageFinderModeTag           1009

@interface MRMessageCenter () 

@property (nonatomic,copy) NSString *activeApplication;

- (void)readNextMessage;
- (void)sendOSVersionMessageToClient;
- (UInt32)getUInt32FromData:(NSData *)data;

- (void)onKeyStroke:(NSData *)data;
- (void)onKeyStrokeWithFlag:(NSData *)data;

- (void)sendBinaryData:(NSData *)data type:(MRBinaryType)type;
- (void)sendBinaryDatas:(NSArray *)datas type:(MRBinaryType)type;
@end


@implementation MRMessageCenter {
    //NSMutableDictionary *_dockNameAndPath;
    BOOL _isRetina;
    SInt32 _osxMinorVersion;
}

@synthesize activeApplication;

+ (MRMessageCenter *)sharedCenter
{
    static MRMessageCenter *sharedCenter;
    static dispatch_once_t token;
    dispatch_once(&token, ^{
        sharedCenter = [[MRMessageCenter alloc] init];
    });
    
    return sharedCenter;
}

- (id)init {
    self = [super init];
    if (self) {
        //_dockNameAndPath = [[NSMutableDictionary alloc] initWithCapacity:10];
    }
    return self;
}

- (void)dealloc {
    self.activeApplication = nil;
    [super dealloc];
}

#pragma mark -- Private Methods --
//- (void)setAppName:(NSString *)name path:(NSString *)path
//{
//    [_dockNameAndPath setObject:path forKey:name];
//}

- (void)sendBinaryDatas:(NSArray *)datas type:(MRBinaryType)type
{
    UInt32 header[3];
    UInt32 totalLength = sizeof(UInt32); //header[2]
    for (NSData *data in datas) {
        totalLength += [data length]+sizeof(UInt32); //part data + part length 
    }
    
    header[0] = kTypeBinaryData ;
    header[1] = totalLength; 
    header[2] = type;
    
    NSMutableData *concatData = [[NSMutableData alloc] init];
    
    [concatData appendBytes:header length:sizeof(UInt32)*3];
    //NSData *headerData = [NSData dataWithBytes:header length:sizeof(UInt32)*3];
    
    for (NSData *data in datas) {
        
        UInt32 length = (UInt32)[data length];
        [concatData appendBytes:&length length:sizeof(UInt32)];
        [concatData appendData:data];
        
                
    }
    
    [[MRServer sharedServer] writeData:concatData tag:0];
    
//    for (NSData *data in datas) {
//        UInt32 length = (UInt32)[data length];
//        [[MRServer sharedServer] writeData:[NSData dataWithBytes:&length length:sizeof(UInt32)] tag:0];
//        [[MRServer sharedServer] writeData:data tag:0];
//        
//    }
    
     
}

- (void)sendBinaryData:(NSData *)data type:(MRBinaryType)type
{
    UInt32 header[3];

    header[0] = kTypeBinaryData ;
    header[1] = (UInt32)[data length] + sizeof(UInt32); //warning
    header[2] = type;
    //NSData *headerData = [NSData dataWithBytes:header length:sizeof(UInt32)*3];
    
    NSMutableData *concatenatedData = [[NSMutableData alloc] init];
    [concatenatedData appendBytes:header length:sizeof(UInt32)*3];
    [concatenatedData appendData:data];
    
    [[MRServer sharedServer] writeData:concatenatedData tag:1];
    [concatenatedData release];
    //[[MRServer sharedServer] writeData:data tag:1];    
}

- (void)sendOSVersionMessageToClient
{
    SInt32 majorVersion,minorVersion;  
     
     Gestalt(gestaltSystemVersionMajor, &majorVersion);
     Gestalt(gestaltSystemVersionMinor, &minorVersion);
     
    _osxMinorVersion = minorVersion;
     NSLog(@"Mac OS X, %d.%d",majorVersion,minorVersion);
     
     UInt32 macOSVersionMessage[3] = {kTypeMacOSVersion,majorVersion,minorVersion};
     NSData *data = [NSData dataWithBytes:macOSVersionMessage length:kMessageTypeLength+kMessageMacOSVersionLength];
     
    [[MRServer sharedServer] writeData:data tag:kMessageMacOSVersionTag];
     
    
}

- (void)sendDockInfo
{
    NSDictionary *defaults = [[NSUserDefaults standardUserDefaults] persistentDomainForName:@"com.apple.dock"];
    
    NSArray *persistentApps = [defaults objectForKey:@"persistent-apps"];
    
    //send Finder first
    NSData *finderdata = [[NSString stringWithString:@"Finder"] dataUsingEncoding:NSUTF8StringEncoding];
    [self sendBinaryData:finderdata type:kTypeDockAppName];
    
    //for (NSDictionary *dockApp in persistentApps) {
    for (NSDictionary *dockAppInfo in persistentApps) {
        NSString *appName = [dockAppInfo valueForKeyPath:@"tile-data.file-label"];
        NSString *appPath = [dockAppInfo valueForKeyPath:@"tile-data.file-data._CFURLString"];
        
        NSLog(@"appName:%@",appName);
        NSLog(@"appPath:%@",appPath);
        
       
        
        if (_osxMinorVersion >= 7) { //Lion
            NSURL *appURL = [NSURL URLWithString:appPath];
            [[DockManager sharedManager] setDockAppName:appName path:[appURL path]];
        }
        else {
            [[DockManager sharedManager] setDockAppName:appName path:appPath];
        }
      
        
        NSLog(@"send dock name:%@",appName);
        NSData *data = [appName dataUsingEncoding:NSUTF8StringEncoding];
        [self sendBinaryData:data type:kTypeDockAppName];
    }
    
 
}

- (void)sendLaunchApplication:(NSString *)name
{
    NSData *data = [name dataUsingEncoding:NSUTF8StringEncoding];
    [self sendBinaryData:data type:kTypeLaunchedAppName];
}

- (void)sendTerminateApplication:(NSString *)name
{
    NSData *data = [name dataUsingEncoding:NSUTF8StringEncoding];
    [self sendBinaryData:data type:kTypeTerminateAppName];
}

- (void)sendActiveApplication:(NSString *)name{
    NSData *data = [name dataUsingEncoding:NSUTF8StringEncoding];
    
    self.activeApplication = name;
    NSLog(@"send active app:%@",name);
    [self sendBinaryData:data type:kTypeActiveApplication];
}

- (void)readNextMessage
{
    [[MRServer sharedServer] readDataToLength:kMessageTypeLength tag:kMessageTypeTag];
}

- (UInt32)getUInt32FromData:(NSData *)data
{
    UInt32 uint32;
    [data getBytes:&uint32 length:sizeof(UInt32)];
    return uint32;
}

- (void)onKeyStroke:(NSData *)data
{
    UInt32 keycode;
    keycode = [self getUInt32FromData:data];
    [[MREventCenter sharedCenter] postKeyboardEventWithKeyCode:keycode];
}

- (void)onKeyStrokeWithFlag:(NSData *)data
{
    UInt32 keycodeAndFlag[3];
    [data getBytes:keycodeAndFlag length:kMessageKeyStrokeWithFlagLength];
    
    [[MREventCenter sharedCenter] postKeyboardEventWithKeyCode:keycodeAndFlag[0] flags:keycodeAndFlag[1] annotated:keycodeAndFlag[2]];
}

- (void)onClientDeviceName:(NSData *)data
{
    NSString *string = [[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSUTF8StringEncoding];
    
    NSLog(@"%@",string);
    [[NSNotificationCenter defaultCenter] postNotificationName:@"ClientDeviceName" object:nil userInfo:[NSDictionary dictionaryWithObject:string forKey:@"name"]];
    
    [string release];
    
}

- (void)onIsRetina:(NSData *)data
{
    _isRetina = [self getUInt32FromData:data];
    
}

- (void)onFinderMode:(NSData *)data
{
    UInt32 mode = [self getUInt32FromData:data];
    [[MREventCenter sharedCenter] setFinderWindowMode:mode];
    
}

- (void)onVolumeUp
{
    NSAppleScript *script = [[NSAppleScript alloc] initWithSource:
                            @"tell current application\n \
                             set volumeSettings to get volume settings\n \
                                set outputVolume to output volume of volumeSettings\n \
                                set volume output volume (outputVolume + 10)\n \
                             end tell\n \
                             "];
    
    [script executeAndReturnError:NULL];
    [script release];
    
    [self readNextMessage];
}

- (void)onVolumeDown
{
    NSAppleScript *script = [[NSAppleScript alloc] initWithSource:
                             @"tell current application\n \
                             set volumeSettings to get volume settings\n \
                             set outputVolume to output volume of volumeSettings\n \
                             set volume output volume outputVolume-10\n \
                             end tell\n"];
                              
                            
    
    NSDictionary *dic;
    [script executeAndReturnError:&dic];
    //NSLog(@"%@",dic);
    [script release];
    
    [self readNextMessage];
}

- (void)onMouseMove:(NSData *)data
{
    int32_t offset[2];
    [data getBytes:offset length:kMessageMouseMoveLength];
    
    [[MREventCenter sharedCenter] postMouseMoveEventWithOffset:CGPointMake(offset[0], offset[1])];
}

- (void)onMouseWheelScroll:(NSData *)data
{
    int32_t offset[2];
    [data getBytes:offset length:kMessageMouseWheelScrollLength];
    
    [[MREventCenter sharedCenter] postScrollWheelEventWithOffset:CGPointMake(offset[0], offset[1])];
}

- (void)onLeftMouseClick
{
    [[MREventCenter sharedCenter] postLeftMouseTapEvent];
    [self readNextMessage];
}

- (void)onRightMouseClick
{
    [[MREventCenter sharedCenter] postRightMouseTapEvent];
    [self readNextMessage];
}

- (void)onLaunchApp:(NSData *)data
{
    NSString *appName = [[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSUTF8StringEncoding];
    
    if ([appName isEqualToString:@"Finder"]) {
        [[NSWorkspace sharedWorkspace] launchApplication:appName];
    }
    else {
        [[DockManager sharedManager] launchAppNamed:appName];
    }
        
        
    [appName release];
}

- (void)onNeedDockInfo
{
    [self sendDockInfo];
}

- (void)onAskingDockAppIcon:(NSData *)data
{
    NSString *appName = [[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSUTF8StringEncoding];
    

    NSData *appNameData = [appName dataUsingEncoding:NSUTF8StringEncoding];
    
    NSData *iconData = [[DockManager sharedManager] iconDataWithAppName:appName retina:_isRetina];
    
    NSLog(@"send dock icon:%@",appName);
    [self sendBinaryDatas:[NSArray arrayWithObjects:appNameData,iconData, nil] type:kTypeAppIcon];
    
    [appName release];
    
}

- (void)onBinaryData:(NSData *)data
{    
    UInt32 dataType = [self getUInt32FromData:data];
    NSData *bodyData = [data subdataWithRange:NSMakeRange(sizeof(UInt32), [data length]-sizeof(UInt32))];
   
    switch (dataType) {
        case kTypeClientDeviceName:
            [self onClientDeviceName:bodyData];
            break;
        
        case kTypeLaunchApp:
            [self onLaunchApp:bodyData];
            break;
            
        case kTypeDockAppName:
            [self onAskingDockAppIcon:bodyData];
            break;
            
        default:
            NSLog(@"wrong binary type");
            break;
    }
}


#pragma mark -- MRServer delegate callback --

- (void)serverDidAcceptNewConnection
{
    NSLog(@"serverDidAcceptNewConnection");
    [self sendOSVersionMessageToClient];
    [self sendDockInfo];
    [self sendActiveApplication:self.activeApplication];
    
    [self readNextMessage];
}

- (void)handleMessageOfType:(MRMessageType)type
{
    switch (type) {
        case kTypeMouseMove:
        [[MRServer sharedServer] readDataToLength:kMessageMouseMoveLength tag:kMessageMouseMoveTag];
        
        break; 
        
        case kTypeMouseWheelScroll:
            [[MRServer sharedServer] readDataToLength:kMessageMouseWheelScrollLength tag:kMessageMouseWheelScrollTag];
            
            break;     
            
        case kTypeKeyStroke:
            [[MRServer sharedServer] readDataToLength:kMessageKeyStrokeLength tag:kMessageKeyStrokeTag];
            
            break;
        case kTypeKeyStrokeWithFlag:
            [[MRServer sharedServer] readDataToLength:kMessageKeyStrokeWithFlagLength tag:kMessageKeyStrokeWithFlagTag];
            
            break;        
            
        case kTypeLeftMouseClick:
            [self onLeftMouseClick];
            
            break;
            
        case kTypeRightMouseClick:
            [self onRightMouseClick];
            
            break;
            
        case kTypeNeedDockInfo:
            [self onNeedDockInfo];
            
            break;           
            
        case kTypeBinaryData:
            [[MRServer sharedServer] readDataToLength:kMessageBinarySizeLength tag:kMessageBinarySizeTag];
            break;
            
        case kTypeIsRetina:
            [[MRServer sharedServer] readDataToLength:kMessageIsRetinaLength tag:kMessageIsRetinaTag];
            
            break;
            
        case kTypeVolumeUp:
            [self onVolumeUp];
            break;
            
        case kTypeVolumeDown:
            [self onVolumeDown];
            break;
        
        case kTypeFinderMode:
            [[MRServer sharedServer] readDataToLength:kMessageFinderModeLength tag:kMessageFinderModeTag];
            
        default:
            NSLog(@"wrong type");
            break;
    }
}

- (void)didReadData:(NSData *)data withTag:(long)tag
{
    UInt32 messageType;
    
    
    if (tag == kMessageTypeTag) { //type data
        
        messageType = [self getUInt32FromData:data];
        
        //handle message
        [self handleMessageOfType:messageType];
        
    }
    else if (tag == kMessageBinarySizeTag) { //binary length data
        
        UInt32 length = [self getUInt32FromData:data];
        [[MRServer sharedServer] readDataToLength:length tag:kMessageBinaryBodyTag];
        
    }
    else { //body data
        
        if (tag == kMessageMouseMoveTag) {
            [self onMouseMove:data];
        }
        else if (tag == kMessageMouseWheelScrollTag) {
            [self onMouseWheelScroll:data];
        }
        else if (tag == kMessageKeyStrokeTag) {
            [self onKeyStroke:data];
        }
        else if (tag == kMessageKeyStrokeWithFlagTag) {
            [self onKeyStrokeWithFlag:data];
        }
        else if (tag == kMessageBinaryBodyTag) {
            [self onBinaryData:data];
        }
        else if (tag == kMessageIsRetinaTag) {
            [self onIsRetina:data];
        }
        else if (tag == kMessageFinderModeTag) {
            [self onFinderMode:data];
        }
        else {
            NSLog(@"wrong tag");
        }
            
        
        [self readNextMessage];
    }
}


@end
